#include "serialize.h"

serialize::serialize(std::ostream *ofile, std::istream *ifile):
	outputFile(ofile), 
	inputFile(ifile), 
	exceptionsOutputBitmask(ofile->exceptions()), 
	exceptionsInputBitmask(ifile->exceptions()){

	outputFile->exceptions(std::ios::badbit);
	inputFile->exceptions(std::ios::badbit);
	flag = true;
	
}

serialize::~serialize(){

	outputFile->exceptions(exceptionsOutputBitmask);
	inputFile->exceptions(exceptionsInputBitmask);

}

void serialize::setOffsets(bool offsets){

	flag = offsets;

}

bool serialize::getOffsets() const{

	return flag;

}

void serialize::initCoordinates(){
	
	float initValue = std::numeric_limits<float>::min();
	
	xyzCoordinates[0] = initValue;
	xyzCoordinates[1] = initValue;
	xyzCoordinates[2] = initValue;
	
}

inline void serialize::getCoordinates(std::ostringstream &ostream, gTypeGo &go, bool relative){

	float value;

	if(go[0] != xyzCoordinates[0]){
		value = go[0];
		if(relative)
			value -= xyzCoordinates[0];
		ostream << " X";
		ostream << std::fixed << std::setprecision(FLOAT_PRECISION) << value;
		xyzCoordinates[0] = go[0];
	}
	if(go[1] != xyzCoordinates[1]){
		value = go[1];
		if(relative)
			value -= xyzCoordinates[1];
		ostream << " Y";
		ostream << std::fixed << std::setprecision(FLOAT_PRECISION) << value;
		xyzCoordinates[1] = go[1];
	}
	if(go[2] != xyzCoordinates[2]){
		value = go[2];
		if(relative)
			value -= xyzCoordinates[2];
		ostream << " Z";
		ostream << std::fixed << std::setprecision(FLOAT_PRECISION) << value;
		xyzCoordinates[2] = go[2];
	}

}

inline void serialize::getCoordinates(std::ostringstream &ostream, gTypeGoCircle &goCircle){
	
	ostream << " I";
	ostream << std::fixed << std::setprecision(FLOAT_PRECISION) << goCircle[3];
	ostream << " J";
	ostream << std::fixed << std::setprecision(FLOAT_PRECISION) << goCircle[4];

}

void serialize::transfer(long offset, long length)
	throw(std::ofstream::failure, std::ifstream::failure){

	char buffer[TRANSFER_BUFFER];
	long len = TRANSFER_BUFFER;
	
	inputFile->seekg(offset, std::ios::beg);
	do{
		length -= TRANSFER_BUFFER;
		if(length < 0)
			len = length + TRANSFER_BUFFER;
		inputFile->read(buffer, len);
		outputFile->write(buffer, len);
	}while(length == TRANSFER_BUFFER);

}

unsigned long serialize::getEndOffset() const 
	throw(std::ifstream::failure){

	inputFile->seekg(0, std::ios::end);
        return inputFile->tellg();

}

void serialize::writeGContainer(gObjectContainer &container) throw(std::ifstream::failure){

	gObjectContainer::iterator iter = container.begin();
	unsigned long startOffset, flagOffset;
	
	startOffset = 0;
	for(; iter != container.end(); iter++){
		transfer(startOffset, (*iter)->getStartOffset() - startOffset);
		flagOffset = outputFile->tellp();
		writeGObject(**iter);
		startOffset = (*iter)->getStartOffset() + (*iter)->getLength();
		if(flag)
                        (*iter)->setOffsets(flagOffset, (unsigned long) outputFile->tellp() - flagOffset);
	}
	transfer(startOffset, getEndOffset() - startOffset);

}

void serialize::writeFeedSpindle(std::ostringstream &ostream, float feed, int spindle, gObject &object){

	bool flag = false;

    if(feed != syntax::initialFeed && object.getFeed() != object.getOriginalFeed()){
		ostream << " F" << feed;
		flag = true;
	}
    if(spindle != syntax::initialSpindle && object.getSpindle() != object.getOriginalSpindle()){
		ostream << " S" << spindle;
		flag = true;
	}
	if(flag)
                ostream << NEW_LINE;

}

void serialize::writeGObject(gObject &object) throw(std::ifstream::failure){

	std::ostringstream ostream;
	gObject::iterator iter = object.begin();
	
	initCoordinates();
	
	//header
        if(object.alter)
            ostream << ";START " << object.getName() << NEW_LINE;
        else
            ostream << "(#OBJECT " << object.getName() << ')' << NEW_LINE;
        writeFeedSpindle(ostream, object.getFeed(), object.getSpindle(), object);
        ostream << "G90" << NEW_LINE;
	*outputFile << ostream.str();
	
	//body
	for(; iter != object.end(); iter++){
		if(iter == object.begin()){
			writeGType(**iter);
			if(object.isForceRelative())
                                *outputFile << "G91" << NEW_LINE;
		}
		else
			writeGType(**iter, object.isForceRelative());
	}
	
	//trailer
	ostream.str("");
        writeFeedSpindle(ostream, object.getOriginalFeed(), object.getOriginalSpindle(), object);
        if(object.isRelative() && !object.isForceRelative())
                ostream << "G91" << NEW_LINE;
        else if(!object.isRelative() && object.isForceRelative())
                ostream << "G90" << NEW_LINE;
        if(object.alter)
            ostream << ";END " << object.getName() << NEW_LINE;
        else
            ostream << "(#!OBJECT " << object.getName() << ')';
	*outputFile << ostream.str();

}

void serialize::writeGType(gType &type, bool relative) throw(std::ifstream::failure){

	std::ostringstream ostream;
	
	if(type.getSymbol() == lexical::token::GO_G){
		gTypeGo &go = dynamic_cast<gTypeGo &>(type);
		ostream << 'G';
		ostream << std::setfill('0') << std::setw(2) << go.getTypeValue();
		getCoordinates(ostream, go, relative);
		if(go.getTypeValue() == 2 || go.getTypeValue() == 3){
			gTypeGoCircle &goCircle = dynamic_cast<gTypeGoCircle &>(type);
			getCoordinates(ostream, goCircle);
		}
	}
	//no others data types are supported
        ostream << NEW_LINE;
	*outputFile << ostream.str();

}
